<?php

namespace app\service;

use app\exception\HttpResponseException;
use app\model\DelayOrderModel;
use app\model\DelayOrderTaskModel;
use app\model\DepartmentModel;
use app\model\EntityModel;
use app\model\EpisodeStepCategorySealTimeModel;
use app\model\ImOptionsModel;
use app\model\PlanModel;
use app\model\ProjectModel;
use app\model\RepairOrderAffectedTaskModel;
use app\model\ReviewRequestModel;
use app\model\TaskModel;
use app\model\TaskRelationTagModel;
use app\model\TaskWorkOrderModel;
use app\model\TenantUserModel;
use app\tool\SingletonTrait;
use support\Request;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\db\Query;

class TaskService
{
    use SingletonTrait;

    /**
     * 任务查询 - 关联项目/部门/环节/用户ID
     * @param int $projectId
     * @param int $departmentId
     * @param array $stepCategoryIdList
     * @param int $userId
     * @param int $tenantId
     * @return TaskModel
     */
    public static function getTaskListBaseQuery(int $projectId, int $departmentId = 0, array $stepCategoryIdList = [], int $userId = 0, int $tenantId = 0)
    {
        $userIdList = [$userId];
        if (!empty($departmentId)) {
            $userIdList = DepartmentService::getInstance()->getDepartmentUserIdList($departmentId);
        }
        $dbr = TaskModel::where('task.project_id', $projectId)
            ->join('task_member', 'task.id = task_member.id')
            ->whereIn('task_member.user_id', $userIdList);
        if (!empty($tenantId)) {
            $dbr = $dbr->where('task.tenant_id', $tenantId);
        }
        if (!empty($stepCategoryIdList)) {
            $trtJoin = TaskRelationTagModel::whereIn('link_module_code', ['step_category', 'show_step_category'])->buildSql();
            $dbr = $dbr->join([$trtJoin => 'task_relation_tag'], 'task_relation_tag.task_id = task.id')
                ->whereIn('task_relation_tag.link_id', $stepCategoryIdList);
        }
        return $dbr;
    }

    /**
     * 用户任务统计详情
     * @param $param
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws HttpResponseException
     */
    public function userCountDetail($param): array
    {
        $unionId = UserService::getInstance()->getUnionIdByUserId($param['user_id']);
        if (empty($unionId)) {
            throw_strack_exception('user not found', -4000000);
        }
        $userInfo = UserService::getInstance()->getOtherUserInfo($unionId);
        if (empty($userInfo)) {
            throw_strack_exception('user not found', -4000000);
        }
        $reqStepCategory = [];
        foreach ($param['step_category_id_list'] ?? [] as $stepCategoryId) {
            if (empty($stepCategoryId)) {
                continue;
            }
            $reqStepCategory[] = $stepCategoryId;
        }

        $param['step_category_id_list'] = $reqStepCategory;

        $extraWhere = '';
        if (!empty($reqStepCategory)) {
            // 排期
            $pq = PlanModel::field('r_episode.link_id as episode_id,r_step_category.link_id as step_category_id')
                ->join("(select * from task_relation_tag where link_module_code in ('step_category','show_step_category')) r_step_category", "r_step_category.task_id = plan.task_id", 'LEFT')
                ->join("(select * from task_relation_tag where link_module_code in ('episode')) r_episode", 'r_episode.task_id = plan.task_id', 'LEFT')
                ->where('plan.scope', 'single')
                ->where('plan.type', 'step_category')
                ->group("r_episode.link_id,r_step_category.link_id");
            if (!empty($param['project_id'])) {
                $pq->whereIn('plan.project_id', $param['project_id']);
            }
            if (!empty($param['start_time']) && !empty($param['end_time'])) {
                $pq->whereBetween('plan.end_time', [$param['start_time'], $param['end_time']]);
            }
            if (!empty($param['step_category_id_list'])) {
                $pq->whereIn('r_step_category.link_id', $param['step_category_id_list']);
            }
            $planEpisodeStepCategoryList = $pq->select();
            if (empty($planEpisodeStepCategoryList->toArray())) {
                return [];
            }
            foreach ($planEpisodeStepCategoryList as $planEpisodeStepCategoryItem) {
                $extraWhere .= "(task.episode_id = {$planEpisodeStepCategoryItem['episode_id']} or r_episode.link_id = {$planEpisodeStepCategoryItem['episode_id']}) and (task.step_category_id = {$planEpisodeStepCategoryItem['step_category_id']} or r_step_category.link_id = {$planEpisodeStepCategoryItem['step_category_id']})";
                $extraWhere .= " OR ";
            }
            $extraWhere = trim($extraWhere, ' OR');
        } else {
            if (!empty($paramp['start_time']) && !empty($paramp['end_time'])) {
                $extraWhere = "plan.end_time between {$paramp['start_time']} and {$paramp['end_time']}";
            }
        }

        $taskTotal = $this->getUserCountDetail($param, $extraWhere);

        $taskDone = 0;
        if ($taskTotal > 0) {
            $extraWhere .= " and (get_json_string(task.`json`, '$.task_status') = 7 or task_member.`status` = 7)";
            $taskDone = $this->getUserCountDetail($param, $extraWhere);
        }

        $newUserInfo = [
            'department_info' => $userInfo['department_info'] ?? null,
            'tenant' => $userInfo['tenant'] ?? null,
            'tenant_position' => $userInfo['tenant'] ?? null,
            'user' => $userInfo['user'] ?? null
        ];
        $returnData['user_info'] = $newUserInfo;
        $returnData['task_total'] = intval($taskTotal);
        $returnData['task_done'] = intval($taskDone);
        $returnData['task_done_percent'] = 0;
        if (!empty($taskDone)) {
            $returnData['task_done_percent'] = intval(floatval($taskDone) / floatval($taskTotal)); // 完成率
        }
        $returnData['task_delivery_percent'] = 0;
        $returnData['task_repair_percent'] = 0;
        $returnData['task_overdue_times'] = 0;
        $returnData['full_situation'] = "";
        return $returnData;

    }

    /**
     * @param $param
     * @param $extraWhere
     * @return int|mixed
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getUserCountDetail($param, $extraWhere)
    {
        $taskFinalDb = (new \app\model\TaskModel)->field("count(distinct task.id) as TaskCount")
            ->join('task_member', 'task_member.task_id = task.id', 'LEFT')
            ->join("(select * from task_relation_tag where link_module_code in ('step_category','show_step_category')) r_step_category", 'r_step_category.task_id = task.id', 'LEFT')
            ->join("(select * from task_relation_tag where link_module_code in ('episode')) r_episode", 'r_episode.task_id = task.id', 'LEFT')
            ->join('plan', 'plan.task_id = task.id', 'LEFT')
            ->where($extraWhere);
        if (!empty($param['user_id'])) {
            $taskFinalDb->where('task_member.partner', $param['user_id']);
        }
        if (!empty($param['project_id'])) {
            $taskFinalDb->whereIn("task.project_id", $param['project_id']);
        }
        if (!empty($param['step_category_id_list'])) {
            $tmpStepCategoryIdList = join(',', $param['step_category_id_list']);
            $taskFinalDb->whereRaw(sprintf("task.step_category_id in (%s) or r_step_category.link_id in (%s)", $tmpStepCategoryIdList, $tmpStepCategoryIdList));
        }
        $taskFinalData = $taskFinalDb->find();
        if (empty($taskFinalData)) {
            return 0;
        }
        return $taskFinalData['TaskCount'];
    }

    /**
     * 获得合同模式
     * @return array|ImOptionsModel[]|Query[]|void
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getBusinessMode()
    {
        $BusinessMode = ImOptionsModel::field("get_json_string(`config`, 'business_mode') as business_mode")
            ->where("name", 'platform_tenant_settings')
            ->find();
        if (empty($BusinessMode->toArray())) {
            return;
        }
        $BusinessMode = $BusinessMode->toArray();
        return $BusinessMode['business_mode'];
    }

    /**
     * 获取该集下的任务统计
     * @param array $stepCategoryIds
     * @param int $episodeId
     * @param int $projectId
     * @return array
     */
    public function getEpisodeTaskStepCategoryProgressData(array $stepCategoryIds, int $episodeId, int $projectId): array
    {
        $progressDataList = ProductionManageService::getInstance()->getEpisodeTaskStepCategoryProgressData(
            null,
            sprintf("link_id in (%s)", join(',', $stepCategoryIds)),
            sprintf("link_id = %d", $episodeId),
            $projectId,
            true
        );
        if (empty($progressDataList)) {
            return [];
        }
        return array_map(function ($tmp) {
            $tmp['step_category_id'] = intval($tmp['step_category_id']);
            $tmp['episode_id'] = intval($tmp['episode_id']);
            $tmp['count'] = intval($tmp['count']);
            $tmp['complete_count'] = intval($tmp['complete_count']);
            $tmp['abnormal_count'] = intval($tmp['abnormal_count']);
            return $tmp;
        }, $progressDataList);
    }

    /**
     * 获取会议列表
     * @param array $stepCategoryIds
     * @param int $episodeId
     * @param int $projectId
     * @return array
     */
    public function getMeetingList(array $stepCategoryIds, int $episodeId, int $projectId): array
    {
        $meetingList = PlanModel::field('plan.id as id,plan.task_id as task_id,max(r_step_category.link_id) as step_category_id,task.name as name')
            ->join('task', 'plan.task_id = task.id')
            ->join('task_relation_tag r_step_category', "plan.task_id = r_step_category.task_id and r_step_category.link_module_code in ('step_category', 'show_step_category')")
            ->join('task_relation_tag r_episode', "task.id = r_episode.task_id and r_episode.link_module_code in ('episode')")
            ->where('plan.project_id', $projectId)
            ->where('r_episode.link_id', $episodeId)
            ->whereIn('r_step_category.link_id', $stepCategoryIds)
            ->where('plan.type', 'meeting')
            ->group('plan.id,plan.task_id,task.name')
            ->select();
        if (empty($meetingList->toArray())) {
            return [];
        }
        return array_map(function ($tmp) {
            $tmp['id'] = intval($tmp['id']);
            $tmp['task_id'] = intval($tmp['task_id']);
            $tmp['step_category_id'] = intval($tmp['step_category_id']);
            return $tmp;
        }, $meetingList->toArray());
    }

    /**
     * 获取排期最大结束时间
     * @param int $projectId
     * @param int $episodeId
     * @return array
     */
    public function getPlanMaxEndTime(int $projectId, int $episodeId): array
    {
        $scMaxEndTime = PlanModel::field('trt.link_id step_category_id,max(plan.end_time) end_time')
            ->join('task_relation_tag trt', "plan.task_id = trt.task_id and trt.link_module_code in ('step_category', 'show_step_category')")
            ->where('plan.project_id', $projectId)
            ->where('plan.scope', 'single')
            ->where('plan.type', 'step_category')
            ->where('plan.entity_id', $episodeId)
            ->group('trt.link_id')
            ->select();

        if (empty($scMaxEndTime->toArray())) {
            return [];
        }
        return array_map(function ($tmp) {
            $tmp['step_category_id'] = intval($tmp['step_category_id']);
            $tmp['end_time'] = intval($tmp['end_time']);
            return $tmp;
        }, $scMaxEndTime->toArray());
    }


    /**
     * 获取剧集计划封板时间
     * @param $projectId
     * @param $episodeId
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getEpisodePlanSealTime($projectId, $episodeId): array
    {
        // 获取剧集计划封板时间
        $episodeStepCategorySealTime = EpisodeStepCategorySealTimeModel::field('id,entity_id,step_category_ids,seal_time')
            ->where('project_id', $projectId)
            ->where('entity_id', $episodeId)
            ->select();
        if (empty($episodeStepCategorySealTime->toArray())) {
            return [];
        }
        $stepCategorySealTimeList = [];
        foreach ($episodeStepCategorySealTime as $item) {
            $stepCategoryIds = explode(',', $item->step_category_ids);
            foreach ($stepCategoryIds as $stepCategoryId) {
                $stepCategorySealTimeList[] = [
                    'id' => intval($item->id),
                    'entity_id' => intval($item->entity_id),
                    'step_category_id' => intval($stepCategoryId),
                    'seal_time' => intval($item->seal_time),
                ];
            }
        }
        return $stepCategorySealTimeList;
    }

    /**
     * 获取实际封板时间
     * @param array $episodeIds
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getEpisodeSealTime(array $episodeIds): array
    {
        $field = [
            'id',
            'name',
            "get_json_string(`entity`.`json`, '$.episode_seal_time') as episode_seal_time"
        ];
        $episodeData = (new \app\model\EntityModel)->field($field)
            ->whereIn('id', $episodeIds)
            ->select();
        if (empty($episodeData->toArray())) {
            return [];
        }
        return array_map(function ($tmp) {
            $tmp['episode_seal_time'] = intval($tmp['episode_seal_time']);
            return $tmp;
        }, $episodeData->toArray());
    }

    /**
     * 我负责的列表
     * @param Request $request
     * @param int $projectId
     * @param array $todoStepCategoryId
     * @param int $startTime
     * @param int $endTime
     * @param array $taskStepType
     * @param int $executor
     * @param bool $isMyResponsible
     * @param int $departmentId
     * @param array $executorUserIdList
     * @param string $dateType
     * @param bool $onlyNotDone
     * @param string $standardOrOther
     * @param string $specialRoleFilter
     * @param string $searchName
     * @param array $page
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function myResponsibleTaskList(
        Request $request,
        int     $projectId,
        array   $todoStepCategoryId,
        int     $startTime,
        int     $endTime,
        array   $taskStepType,
        int     $executor,
        bool    $isMyResponsible,
        int     $departmentId,
        array   $executorUserIdList,
        string  $dateType,
        bool    $onlyNotDone,
        string  $standardOrOther,
        string  $specialRoleFilter,
        string  $searchName,
        array   $page
    ): array
    {
        $res = [];

        $currentTime = time();
        $rStepCategoryJoined = false;

        $departmentService = DepartmentService::getInstance();

        $statusService = StatusService::getInstance();
        $countStatusIdList = $statusService->byCodeMap(['not_started', 'ready_to_start', 'wait_assemble'], false, 'id');

        if (!empty($departmentId) || !empty($todoStepCategoryId)) {
            // 按组划分
            $departmentUserIdList = [];
            if (!empty($departmentId)) {
                $departmentUserIdList = $departmentService->getDepartmentUserIdList($departmentId);
                if (empty($departmentUserIdList)) {
                    return $res;
                }
            }

            $roatDbr = RepairOrderAffectedTaskModel::group('task_id')->field("count(id) as rn,task_id")->buildSql();
            $twoDbr = TaskWorkOrderModel::where('status', 0)->group('task_id')->field('task_id')->buildSql();

            $dbr = TaskModel::join('task_member', 'task.id = task_member.task_id')
                ->join('user', 'user.id = task_member.partner')
                ->join([$roatDbr => 'roat'], 'task.id = roat.task_id', 'left')
                ->join([$twoDbr => 'task_work_order_temp'], 'task_work_order_temp.task_id = task.id', 'left')
                ->order('user.id')
                ->field([
                    'user.id as id',
                    'count(distinct task.id) as task_num',
                    'task_member.partner',
                    'user.name as name',
                    'count(get_json_string(task.`json`, "$.task_status") in (7,8)) as final_task_num',
                    'truncate(sum(task.actual_man_hour),1) as final_work_time',
                    'sum(roat.rn) as repair_num'
                ])
                ->fieldRaw('count('
                    . "("
                    . " task.step_type = 'step' and ("
                    . " task.assign_status = 'default' or get_json_string(task.`json`, '$.task_status') in (" . $countStatusIdList . ")"
                    . " ) and get_json_string(task.`json`, '$.task_status') in (3,7) "
                    . " ) "
                    . " or ( task.plan_end_time between 1 and " . $currentTime . " and get_json_string(task.`json`, '$.task_status') not in (3,7) ) "
                    . " or task_work_order_temp.task_id > 0 "
                    . " ) as abnormal_num "
                );

            if (!empty($searchName)) {
                $dbr = $dbr->where('user.name', 'LIKE', '%' . $searchName . "%");
            }
            if (!empty($executorUserIdList)) {
                $dbr = $dbr->whereIn('task_member.partner', $executorUserIdList);
            }
            if (!empty($departmentUserIdList)) {
                $dbr = $dbr->whereIn('task_member.partner', $departmentUserIdList);
            }
            $dbr = $dbr->group('task_member.partner,user.id,user.name');
        } else {
            // 默认按部门划分
            $roatDbr = RepairOrderAffectedTaskModel::group('task_id')->field("count(id) as rn,task_id")->buildSql();
            $twoDbr = TaskWorkOrderModel::where('status', 0)->group('task_id')->field('task_id')->buildSql();

            $dbr = TaskModel::join('task_member', 'task.id = task_member.task_id')
                ->join('user', 'user.id = task_member.partner')
                ->join('tenant_user', 'tenant_user.user_id = user.union_id')
                ->join('tenant_user_department', 'tenant_user_department.tenant_user_id = tenant_user.id')
                ->join(['department' => 'd'], 'tenant_user_department.department_id = d.id', 'inner')
                ->join([$roatDbr => 'roat'], 'task.id = roat.task_id', 'left')
                ->join([$twoDbr => 'task_work_order_temp'], 'task_work_order_temp.task_id = task.id', 'left')
                ->order('d.id')
                ->field([
                    'd.id as id',
                    'd.name as name',
                    'count(distinct task.id) as task_num',
                    'count(get_json_string(task.`json`, "$.task_status") in (7,8) ) as final_task_num',
                    'truncate(sum(task.actual_man_hour),1) as final_work_time',
                    'sum(roat.rn) as repair_num',
                ])
                ->fieldRaw('count( '
                    . "(("
                    . " task.step_type = 'step' and ("
                    . " task.assign_status = 'default' or get_json_string(task.`json`, '$.task_status') in (" . $countStatusIdList . ") )"
                    . " ) and task.plan_end_time between 1 and " . $currentTime . " and get_json_string(task.`json`, '$.task_status') not in (3,7) "
                    . " ) "
                    . " or ( task.plan_end_time between 1 and " . $currentTime . " and get_json_string(task.`json`, '$.task_status') not in (3,7) ) "
                    . " or task_work_order_temp.task_id > 0 "
                    . " ) as abnormal_num "
                );
            if (!empty($searchName)) {
                $dbr = $dbr->where('user.name', 'LIKE', '%' . $searchName . "%");
            }

            if (!empty($specialRoleFilter)) {
                $userService = UserService::getInstance();
                $userInfo = $userService->getMyUserInfo();
                if ($specialRoleFilter == 'artist') {
                    $departmentService = DepartmentService::getInstance();
                    $departmentManagerDepartmentList = $departmentService->getUserDepartmentListInfo($userInfo['user']['id'], $userInfo['tenant']['id'], ["headman", "step_producer"]);
                    $departmentManagerDepartmentIdList = array_column($departmentManagerDepartmentList, 'id');
                    if (!empty($departmentManagerDepartmentIdList)) {
                        //db.Where("task.department_id in ?", departmentManagerDepartmentIdList)
                        $departmentUserIdList = TenantUserModel::join('user', 'user.union_id = tenant_user.user_id')
                            ->join('tenant_user_department', 'tenant_user_department.tenant_user_id = tenant_user.id')
                            ->whereIn('tenant_user_department.department_id', $departmentManagerDepartmentIdList)
                            ->column('distinct user.id uid');
                        if (empty($departmentUserIdList)) {
                            return $res;
                        }
                        $dbr = $dbr->whereIn('task_member.partner', $departmentUserIdList)
                            ->whereIn('d.id', $departmentManagerDepartmentIdList);
                    } else {
                        return $res;
                    }
                }
            }

            $dbr = $dbr->group('d.id,d.name');
        }

        switch ($dateType) {
            case 'delivery':
                $dbr = $dbr->whereBetween('task.plan_delivery_time', [$startTime, $endTime]);
                break;
            case 'delivery_actually':           // 实际交付筛选
                $dbr = $dbr->whereBetween('task.delivery_time', [$startTime, $endTime])
                    ->whereFieldRaw('get_json_string(task.`json`, "$.task_status")', 7);
                break;
            default:
                if (!empty($startTime) && !empty($endTime)) {
                    $dbr = $dbr->whereBetween('task.plan_end_time', [$startTime, $endTime]);
                }
        }

        if (!empty($projectId)) {
            $dbr = $dbr->where('task.project_id', $projectId);
        }

        if (!empty($page) && count($page) > 1) {
            $pageNum = array_shift($page);
            $pageSize = array_shift($page);
            $dbr = $dbr->page($pageNum, $pageSize);
        }

        if (!empty($taskStepType)) {
            $dbr = $dbr->whereIn('task.step_type', $taskStepType);
        }

        if (!empty($onlyNotDone)) {
            $dbr = $dbr->whereFieldRaw('get_json_string(task.`json`, "$.task_status")', '<>', $statusService->byCode('final'));
        }

        if (!empty($todoStepCategoryId)) {
            if (!$rStepCategoryJoined) {
                $rscJoin = TaskRelationTagModel::whereIn('link_module_code', ['step_category', 'show_step_category'])->buildSql();
                $dbr = $dbr->join([$rscJoin => 'r_step_category'], 'r_step_category.task_id = task.id');
                $rStepCategoryJoined = true;
            }
            $dbr = $dbr->whereIn('r_step_category.link_id', $todoStepCategoryId);
        }

        $taskFilter = ['task.step_type' => ['NEQ', 'standard_make_to_do']];
        $res = $dbr->where($taskFilter)->select()->toArray();
        return result_array_construct([
            'id' => 'intval',
            'name' => 'strval',
            'task_num' => 'intval',
            'final_task_num' => 'intval',
            'repair_num' => 'intval',
            'delay_num' => 'intval',
            'final_work_time' => 'strval',
            'abnormal_num' => 'intval',
        ], $res);
    }

    /**
     * 我负责统计
     * @param Request $request
     * @param int $projectId
     * @param string $specialRoleFilter
     * @param array $todoStepCategoryId
     * @param int $startTime
     * @param int $endTime
     * @param array $taskStepType
     * @param int $executor
     * @param bool $isMyResponsible
     * @param int $departmentId
     * @param array $executorUserIdList
     * @param string $dateType
     * @param bool $onlyNotDone
     * @param string $standardOrOther
     * @param string $searchName
     * @param array $page
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function myResponsibleTaskCount(
        Request $request,
        int     $projectId,
        string  $specialRoleFilter,
        array   $todoStepCategoryId,
        int     $startTime,
        int     $endTime,
        array   $taskStepType,
        int     $executor,
        bool    $isMyResponsible,
        int     $departmentId,
        array   $executorUserIdList,
        string  $dateType,
        bool    $onlyNotDone,
        string  $standardOrOther,
        string  $searchName,
        array   $page
    ): array
    {
        $res = [];
        $isMyResponsible = true;
        $departmentService = DepartmentService::getInstance();
        $userService = UserService::getInstance();

        if (!empty($departmentId)) {
            $executorUserIdList = $departmentService->getDepartmentUserIdList($departmentId);
            if (empty($executorUserIdList)) {
                return $res;
            }
            $departmentId = 0;
        } else if (empty($todoStepCategoryId)) {
            $departmentManagerDepartmentIdList = [];
            $userInfo = $userService->getMyUserInfo();
            foreach ($userInfo['department_info'] ?? [] as $val) {
                if (in_array($val['role_code'], ["headman", "step_producer"])) {
                    $departmentManagerDepartmentIdList[] = $val['department_id'];
                }
            }
            $executorUserIdList = $departmentService->getDepartmentUserIdList($departmentManagerDepartmentIdList);
        }

        $todayCountData = ProductionManageService::getInstance()->todayCountForMyResponsibleTask(
            $projectId,
            $specialRoleFilter,
            $todoStepCategoryId,
            $startTime,
            $endTime,
            $taskStepType,
            $executor,
            $isMyResponsible,
            $departmentId,
            $executorUserIdList,
            $dateType,
            $onlyNotDone,
            $standardOrOther
        );

        // 完成率
        $finalRate = empty($todayCountData['total']) ? 0 : (floor($todayCountData['done'] * 10000 / $todayCountData['total']) / 100);
        // 返修率
        $repairRate = empty($todayCountData['total']) ? 0 : (floor($todayCountData['repair_num'] * 10000 / $todayCountData['total']) / 100);

        $res = [
            'final_rate' => $finalRate,                                     // 完成率
            'repair_rate' => $repairRate,                                   // 返修率
            'repair_num' => $todayCountData['repair_num'],                  // 返修
            'task_total_num' => $todayCountData['total'],                   // 任务总数
            'task_final_num' => $todayCountData['done'],                    // 已完成的
            'progress_num' => $todayCountData['in_progress'],               // 进行中的
            'not_final_num' => $todayCountData['not_final_num'],            // 未完成
            'not_start_num' => $todayCountData['not_start_num'],            // 未开始
            'abnormal_num' => $todayCountData['abnormal'],                  // 异常任务
            'delay_final_num' => 0,                                         // 暂时不做
            'delay_rate' => 0,                                              // 暂时不做
        ];

        return $res;
    }

    /**
     * 任务统计
     * @param Request $request
     * @param array $param
     * @return int[]
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function count(Request $request, array $param): array
    {
        $res = [
            'total' => 0,
            'done' => 0,
            'un_done' => 0,
            'abnormal' => 0,
        ];
        $currentTime = time();
        $departmentService = DepartmentService::getInstance();
        $departmentUserIdList = [];
        if (!empty($param['department_id'])) {
            $departmentUserIdList = $departmentService->getDepartmentUserIdList($param['department_id']);
            if (empty($departmentUserIdList)) {
                return $res;
            }
        }

        $userService = UserService::getInstance();
        $userInfo = $userService->getMyUserInfo();
        $taskIds = [];
        if ($param['date_type'] == 'delay_created') {
            $dbr = ReviewRequestModel::where('module_id', ModuleService::getInstance()->byCode('task', 'id'))
                ->whereBetween('created', [$param['start_time'], $param['end_time']]);
            if (!empty($param['project_id'])) {
                $dbr = $dbr->where('review_request.project_id', $param['project_id']);
            }
            $taskIds = $dbr->column("distinct link_id");
            if (empty($taskIds)) {
                return $res;
            }
        }

        if ($param['standard_or_other'] == "yes" && !empty($param['todo_step_category_id'])) {
            $departmentUserIdList = TenantUserModel::join("user", "user.union_id = tenant_user.user_id")
                ->join("tenant_user_department", "tenant_user.id = tenant_user_department.tenant_user_id")
                ->join("department", "department.id = tenant_user_department.department_id")
                ->whereFieldRaw("array_length(array_intersect([" . implode(',', $param['todo_step_category_id']) . "], split(department.step_category_ids, ',')))", ">", 0)
                ->where('tenant_user.tenant_id', $request->getTenantId())
                ->column("distinct user.id");
            if (empty($departmentUserIdList)) {
                return $res;
            }
        }

        $statusSql = StatusService::getInstance()->byCodeMap(['not_started', 'ready_to_start', 'wait_assemble'], false, 'id');
        $stoppedStatusSql = StatusService::getInstance()->byCodeMap(['closed', 'final'], false, 'id');
        $abnormalConditionSql = 'task_work_order_temp.task_id is not null '
            . ' or ('

//            . ' task.step_type = "step" '
//            . ' and ('
//            . ' task.assign_status = "default" or '
//            . ' get_json_string(task.`json`, "$.task_status") in ('
//            . $statusSql
//            . ')) and '

            . ' task.plan_end_time between 1 and ' . $currentTime
            . ' and get_json_string(task.`json`, "$.task_status") not in (' . $stoppedStatusSql . ') '
            . ')';

        $twoJoin = TaskWorkOrderModel::field("task_id")->where('status', 0)->group('task_id')->buildSql();
        $dateTaskStatusSql = StatusService::getInstance()->byCodeMap($param['date_type'] == "delivery" ? ['final'] : ['final', 'ready_to_publish'], false, 'id');
        $dbr = $this->getCountBaseQuery($request, $param, $userInfo, $taskIds, $departmentUserIdList)
            ->join([$twoJoin => 'task_work_order_temp'], 'task_work_order_temp.task_id = task.id', 'left');
        $count = $dbr->field(' count(distinct task.id) as total, '
            . ' count(distinct if(get_json_string(task.`json`, "$.task_status") in (' . $dateTaskStatusSql . '), task.id, null)) as done, '
            . ' count(distinct if(' . $abnormalConditionSql . ', task.id, null)) as abnormal ')->select()->toArray();
        $count = array_shift($count);

        $res['total'] = intval($count['total']);
        $res['done'] = intval($count['done']);
        $res['abnormal'] = intval($count['abnormal']);
        return $res;
    }

    /**
     * 组装统计Model
     * @param Request $request
     * @param array $param
     * @return Query
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function getCountBaseQuery(Request $request, array $param, array $userInfo, array $delayTaskIds, array $departmentUserIdList): Query
    {
        $joinMap = [];
        $res = new TaskModel();
        if (!empty($userInfo['tenant']['id'] ?? 0)) {
            $res = $res->where('task.tenant_id', $userInfo['tenant']['id']);
        }
        if (!empty($param['task_step_type'])) {
            $res = $res->whereIn("task.step_type", $param['task_step_type']);
        }
        if (!empty($param['task_status_code'])) {
            $res = $res->whereRaw("get_json_string(task.`json`, '$.task_status') in (" . StatusService::getInstance()->byCodeMap($param['task_status_code'], false, 'id') . ")");
        }
        if (!empty($param['todo_step_category_id'])) {
            if (!isset($joinMap['r_step_category'])) {
                $rscJoin = TaskRelationTagModel::whereIn('link_module_code', ['step_category', 'show_step_category'])->buildSql();
                $res = $res->join([$rscJoin => 'r_step_category'], 'r_step_category.task_id = task.id');
                $joinMap['r_step_category'] = true;
            }
            $res = $res->whereIn('r_step_category.link_id', $param['todo_step_category_id']);
        }
        if (!empty($param['project_id'])) {
            $res = $res->where('task.project_id', $param['project_id']);
        }
        if (!empty($param['executor_user_id_list'])) {
            if (!isset($joinMap['task_member'])) {
                $res = $res->join('task_member', 'task_member.task_id = task.id');
                $joinMap['task_member'] = true;
            }
            $res = $res->whereIn('task_member.partner', $param['executor_user_id_list']);
        }
        if (!empty($departmentUserIdList)) {
            if (!isset($joinMap['task_member'])) {
                $res = $res->join('task_member', 'task_member.task_id = task.id');
                $joinMap['task_member'] = true;
            }
            $res = $res->whereIn('task_member.partner', $departmentUserIdList);
        }
        if (!empty($param['special_role_filter'])) {
            if (!isset($joinMap['task_member'])) {
                $res = $res->join('task_member', 'task_member.task_id = task.id');
                $joinMap['task_member'] = true;
            }
            $userInfo = UserService::getInstance()->getMyUserInfo(); // todo 建议改为前端传过来当前用户信息
            $projectMemberUserIdList = [];
            if ($param['special_role_filter'] == 'director') {
                $projectMemberUserIdList = UserService::getInstance()->getRoleDepartmentIds($param['project_id'], 'director');
            }
            ProductionManageService::getInstance()->getSpecialRoleFilterQuery(['special_role_filter' => $param['special_role_filter']], $userInfo, $projectMemberUserIdList, $res);
        }

        if ($param['history'] == 'yes') {
            $param['date_type'] = 'history_' . $param['date_type'];
        }
        switch ($param['date_type']) {
            case 'in_progress_full':
                $res = $res->whereFieldRaw('get_json_string(task.`json`, "$.task_status")',
                    'NOT IN',
                    StatusService::getInstance()->byCodeMap([
                        'closed', 'final', 'ready_to_publish',
                        'remake',
                        'submitted', 'wait_for_party_a_review', 'wait_partyb_submit',
                    ], false, 'id'))
                    ->whereRaw('task.plan_end_time between ' . $param['start_time'] . ' and ' . $param['end_time']
                        . ' or get_json_string(task.`json`, "$.task_status") NOT IN (' . StatusService::getInstance()->byCodeMap(["not_started", "ready_to_start", "wait_assemble", "draft"], false, 'id') . ')');
                break;
            case 'in_plan_overdue':
                $res = $res->whereBetween('task.plan_end_time', [$param['start_time'], $param['end_time']])
                    ->whereRaw('get_json_string(task.`json`, "$.task_status") NOT IN (' . StatusService::getInstance()->byCodeMap(['ready_to_publish', 'closed', 'final'], false, 'id') . ')');
                break;
            case 'demand_add':                          // 新增
                $res = $res->whereBetween('task.created', [$param['start_time'], $param['end_time']])
                    ->whereFieldRaw('get_json_string(task.`json`, "$.task_is_append")', 'yes');
                break;
            case 'delay_created':                       // 延期
                $res = $res->whereIn('task.id', $delayTaskIds);
                break;
            case 'delivery_actually':                   // 实际交付筛选
                $res = $res->whereBetween('task.delivery_time', [$param['start_time'], $param['end_time']])
                    ->whereFieldRaw('get_json_string(task.`json`, "$.task_status")', 7);
                break;
            case 'delivery':
                // 定义: 应交付计划
                // 条件: 1. 任务计划交付时间 落在区间
                $res = $res->whereBetween('task.plan_delivery_time', [$param['start_time'], $param['end_time']]);
                break;
            case 'history_delivery':
                // 定义: 应交付计划 历史
                // 条件: 1. 任务计划交付时间 落在区间
                //      2. 任务关联延期单，已确认的，原计划交付时间 落在区间
                $res = $res->join('delay_order_task', 'delay_order_task.task_id = task.id and manager_confirmed = "yes"', 'left')
                    ->whereRaw(
                        'task.plan_delivery_time between ' . $param['start_time'] . ' and ' . $param['end_time']
                        . ' or delay_order_task.plan_delivery_time between ' . $param['start_time'] . ' and ' . $param['end_time']
                    );
                break;
            case 'history_':
            case 'history_done':
                // 定义: 应完成计划 历史
                // 条件: 1. 任务计划完成时间 落在区间
                //      2. 任务关联延期单，已确认的，原计划完成时间 落在区间
                $res = $res->join('delay_order_task', 'delay_order_task.task_id = task.id and manager_confirmed = "yes"', 'left')
                    ->whereRaw('task.plan_end_time between ' . $param['start_time'] . ' and ' . $param['end_time']
                        . ' or delay_order_task.original_plan_time between ' . $param['start_time'] . ' and ' . $param['end_time']);
                break;
            case 'done':
                // 定义: 应完成计划 实时
                // 条件: 1. 任务计划完成时间 落在区间
            default:
                $res = $res->whereBetween('task.plan_end_time', [$param['start_time'], $param['end_time']]);
        }
        if ($param['only_not_done'] == 'yes') {
            $res = $res->whereFieldRaw('get_json_string(task.`json`, "$.task_status")', '<>', StatusService::getInstance()->byCode('final', 'id'));
        }
        return $res->whereIn('task.step_type', [
            TaskModel::STEP_TYPE_STEP,
            TaskModel::STEP_TYPE_TODO,
            TaskModel::STEP_TYPE_WORK_ORDER_TO_DO,
            TaskModel::STEP_TYPE_ENTITY_TO_DO,
            TaskModel::STEP_TYPE_STANDARD_MEETING,
            TaskModel::STEP_TYPE_STANDARD_REVIEW,
        ])
            ->whereFieldRaw('get_json_string(task.`json`, "$.task_status")', '<>', StatusService::getInstance()->byCode('closed', 'id'));
    }


    /**
     * 处理延期统计筛选条件
     * @param $q
     * @param $param
     * @return mixed
     */
    public function delayCountWhereQuery($q, $param)
    {
        $q->whereIn("dot.type", ['request_delay', 'work_order']);
        if (!empty($param['todo_step_category_id_list'])) {
            if ($param['query_range'] === 'upstream') {
                $q->whereIn('dotscc.step_category_id', $param['todo_step_category_id_list']);
            } else {
                $q->whereIn('dot.step_category_id', $param['todo_step_category_id_list']);
            }
        }
        if (!empty($param['todo_step_category_id'])) {
            if ($param['query_range'] === 'upstream') {
                $q->whereIn('dotscc.step_category_id', $param['todo_step_category_id']);
            } else {
                $q->whereIn('dot.step_category_id', $param['todo_step_category_id']);
            }
        }
        if (!empty($param['project_id'])) {
            $q = $q->where('dot.project_id', $param['project_id']);
        }
        if (!empty($param['start_time'])) {
            $q = $q->where('dot.created', '>=', $param['start_time']);
        }
        if (!empty($param['end_time'])) {
            $q = $q->where('dot.created', '<=', $param['end_time']);
        }
        return $q;
    }

    /**
     * 任务延期统计
     * @param Request $request
     * @param array $param
     * @return int[]
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function delayCount(Request $request, array $param): array
    {
        //判断部门
        if (!empty($param['department_id'])) {
            $department = (new DepartmentModel())->where('id', $param['department_id'])->find();
            $param['todo_step_category_id_list'] = explode(',', $department['step_category_ids']);
            if (empty($param['todo_step_category_id_list'])) {
                return [
                    'total' => 0,
                    'pass' => 0,
                    'not_pass' => 0,
                    'affected' => 0,
                ];
            }
        }

        //查询是否外包项目
        $project_id = $param['project_id'] ?? 0;
        $projectInfo = (new ProjectModel())->where('id', $project_id)->field('id,type,belong_project_id')->find();
        $isOutSource = ($projectInfo['type'] == 'outsource') ? true : false;
        if ($isOutSource) {
            $param['project_id'] = $projectInfo['belong_project_id'] ?? 0;
        }

        //判断查询范围
        switch ($param['query_range']) {
            //我的
            case 'mine':
                return $this->mineDelayCount($param, $request->getSaasUserId());
            //上游
            case 'upstream':
                return $this->getUpstreamDelayCount($param, $param['query_type']);
            //默认--本环节
            default:
                return $this->getDelayCount($param, $param['query_type']);
        }
    }

    /**
     * 本环节--任务延期统计
     * @param array $param 查询数组
     * @param string $queryType 查询类型
     * @param bool $isOutSource 是否外包项目
     * @return array
     */
    private function getDelayCount(array $param, string $queryType): array
    {
        $notPassed = [];
        //查询影响
        if ($queryType == 'affected') {
            // 本环节延期产生影响的
            $passed = $this->deskDelayCount($param, 'affected');
        } else {
            //工作台--确认延期情况
            $passed = $this->deskDelayCount($param, 'confirmed_yes');
            //工作台--未确认延期
            $notPassed = $this->deskDelayCount($param, 'confirmed_no');
        }
        //整理返回数组
        return $this->clearDelayCountData($passed, $notPassed);
    }

    /**
     * 上游--任务延期统计
     * @param array $param 查询数组
     * @param string $queryType 查询类型
     * @return array
     */
    private function getUpstreamDelayCount(array $param, string $queryType): array
    {
        $notPassed = [];
        //查询影响
        if ($queryType == 'affected') {
            // 本环节延期产生影响的
            $passed = $this->deskDelayCount($param, 'upstream_affected');
        } else {
            //工作台--确认延期情况
            $passed = $this->deskDelayCount($param, 'upstream_confirmed_yes');
            //工作台--未确认延期
            $notPassed = $this->deskDelayCount($param, 'upstream_confirmed_no');
        }
        //整理返回数组
        return $this->clearDelayCountData($passed, $notPassed);
    }


    /**
     * 我的--任务延期统计
     * @param array $param 查询数组
     * @param int $userId 用户ID
     * @return array
     */
    private function mineDelayCount(array $param, int $userId): array
    {
        $closedStatusId = StatusService::getInstance()->byCode('closed')['id'];
        // 我的
        $mine = (new DelayOrderModel())
            ->alias('do')
            ->join('delay_order_task dot', 'do.id = dot.delay_order_id')
            ->join('task', 'task.id = dot.task_id');
        $mine = $this->delayCountWhereQuery($mine, $param);
        $mine = $mine
            ->whereRaw("get_json_string(task.`json`, '$.task_status') <> $closedStatusId")
            ->where('dot.created_by', $userId)
            ->group('dot.manager_confirmed')
            ->field('dot.manager_confirmed s,count(dot.id) c')
            ->select();
        $mine = $mine->column('c', 's');

        $notPass = $mine['default'] ?? 0;
        $pass = $mine['yes'] ?? 0;
        if (!empty($mine['no'])) {
            $pass += $mine['no'];
        }
        $total = $pass + $notPass;

        return [
            'total' => $total,
            'pass' => $pass,
            'not_pass' => $notPass,
            'affected' => 0,
        ];
    }

    /**
     * 整理返回数组
     */
    private function clearDelayCountData(array $passed, array $notPassed): array
    {
        //整理返回数组
        $res['pass'] = $passed['count'] ?? 0;
        $res['not_pass'] = $notPassed['count'] ?? 0;
        //新方案--增加
        $res['pass_task_ids'] = $passed['list'] ?? [];
        $res['not_pass_task_ids'] = $notPassed['list'] ?? [];
        $res['total_task_ids'] = array_merge($res['pass_task_ids'], $res['not_pass_task_ids']);
        $res['total'] = $res['pass'] + $res['not_pass'];

        return $res;
    }

    /**
     * 工作台--延期情况统计
     * @param array $param 查询条件
     * @param string $type 查询类型 confirmed_yes=确认
     *                             confirmed_no=未确认
     *                             upstream_confirmed_yes=上游确认
     *                             upstream_confirmed_no=上游未确认
     *                             affected=延期影响--情况
     *                             upstream_affected=上游延期影响--情况
     * @return array
     */
    private function deskDelayCount(array $param, string $type): array
    {
        //限制返回条数
        $limit = 1000;

        // 查询条件
        $baseSql = (new DelayOrderTaskModel())
            ->alias('dot')
            ->join('delay_order_task_step_category_confirm dotscc', 'dot.id = dotscc.delay_order_task_id')
            ->join('task', 'task.id = dot.task_id');
        $baseSql = $this->delayCountWhereQuery($baseSql, $param);

        //过滤任务状态 、 类型
        $closedStatusId = StatusService::getInstance()->byCode('closed')['id'];
        $baseSql->whereRaw("get_json_string(task.`json`, '$.task_status') <> $closedStatusId")
            ->whereRaw("task.step_type not in ('template_to_do', 'standard_make_to_do') ");

        //判断查询类型
        switch ($type) {
            case 'confirmed_yes':
                $baseSql->where('dotscc.is_self_step_category', 'yes')
                    ->where('dotscc.is_confirmed', 'yes');
                break;
            case 'confirmed_no':
                $baseSql->where('dotscc.is_self_step_category', 'yes')
                    ->where('dotscc.is_confirmed', 'no')
                    ->where('dotscc.is_active', 'yes');
                break;
            case 'upstream_confirmed_yes':
                $baseSql->where('dotscc.is_self_step_category', 'no')
                    ->where('dotscc.is_confirmed', 'yes');
                break;
            case 'upstream_confirmed_no':
                $baseSql->where('dotscc.is_self_step_category', 'no')
                    ->where('dotscc.is_confirmed', 'no')
                    ->where('dotscc.is_active', 'yes');
                break;
            //延期影响--情况
            case 'affected':
                $baseSql->where('dotscc.is_self_step_category', 'yes')
                    ->where('dotscc.is_confirmed', 'yes')
                    ->where('dot.manager_confirmed', 'yes');
                break;
            case 'upstream_affected':
                $baseSql->where('dotscc.is_self_step_category', 'no')
                    ->where('dotscc.is_confirmed', 'yes')
                    ->where('dotscc.need_delay', 'yes')
                    ->where('dotscc.delay_to', '<>', 0)
                    ->where('dot.manager_confirmed', 'yes');
                break;
            default:
                return [];
        }
        $res = $baseSql->field('dot.task_id')->limit($limit)->column('distinct dot.task_id');

        //外包项目--传换真实任务ID

        return [
            'count' => count($res),
            'list' => $res
        ];
    }


    /**
     * 人员列表
     * @param Request $request
     * @param int $projectId
     * @param string $specialRoleFilter
     * @param array $todoStepCategoryId
     * @param int $startTime
     * @param int $endTime
     * @param array $taskStepType
     * @param int $executor
     * @param bool $isMyResponsible
     * @param int $departmentId
     * @param array $executorUserIdList
     * @param string $dateType
     * @param bool $onlyNotDone
     * @param string $standardOrOther
     * @param string $searchName
     * @param array $page
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function userList(
        Request $request,
        int     $projectId,
        string  $specialRoleFilter,
        array   $todoStepCategoryId,
        int     $startTime,
        int     $endTime,
        array   $taskStepType,
        int     $executor,
        bool    $isMyResponsible,
        int     $departmentId,
        array   $executorUserIdList,
        string  $dateType,
        bool    $onlyNotDone,
        string  $standardOrOther,
        string  $searchName,
        array   $page
    ): array
    {
        $res = [];
        $currentTime = time();
        $rStepCategoryJoined = false;

        $roatJoin = RepairOrderAffectedTaskModel::group('task_id')
            ->field('count(id) as rn'
                . ', task_id')
            ->buildSql();
        $twoJoin = TaskWorkOrderModel::group('task_id')
            ->where('status', 0)
            ->field('task_id')
            ->buildSql();
        $dbr = TaskModel::fieldRaw('user.id as id'
            . ', user.name as name'
            . ', task_member.partner'
            . ', count(distinct task.id) as task_num'
            . ', count(get_json_string(task.`json`, "$.task_status") in (' . StatusService::getInstance()->byCodeMap(['final', 'ready_to_publish'], false, 'id') . ') or null) as final_task_num'
            . ', truncate(sum(task.actual_man_hour), 1) as final_work_time'
            . ', sum(roat.rn) as repair_num'
            . ', count( ( ('
//            . ' task.step_type = "step" '
//            . ' and ( task.assign_status = "default" '
//            . ' or get_json_string(task.`json`, "$.task_status") in (' . StatusService::getInstance()->byCodeMap(['not_started', 'ready_to_start', 'wait_assemble'], false, 'id') . ') '
//            . ' ) and '
            . ' task.plan_end_time between ' . $startTime . ' and ' . $endTime
            . ' and get_json_string(task.`json`, "$.task_status") not in (' . StatusService::getInstance()->byCodeMap(['final', 'closed'], false, 'id') . ') '
            . ') or task_work_order_temp.task_id is not null '
            . ') or null ) as abnormal_num')
            ->join('task_member', 'task.id = task_member.task_id')
            ->join('user', 'user.id = task_member.partner')
            ->join([$roatJoin => 'roat'], 'task.id = roat.task_id', 'left')
            ->join([$twoJoin => 'task_work_order_temp'], 'task_work_order_temp.task_id = task.id', 'left')
            ->where('task.tenant_id', $request->getTenantId())
            ->where('task.step_type', '<>', 'step_category')
            ->whereFieldRaw('get_json_string(task.`json`, "$.task_status")', '<>', StatusService::getInstance()->byCode('closed', 'id'))
            ->group('task_member.partner, user.id, user.name');

        $departmentUserIdList = [];
        if (!empty($departmentId)) {
            $departmentUserIdList = DepartmentService::getInstance()->getDepartmentUserIdList($departmentId);
            if (empty($departmentUserIdList)) {
                return $res;
            }
        }
        if (!empty($searchName)) {
            $dbr = $dbr->where('user.name', 'LIKE', '%' . $searchName . '%');
        }
        if (!empty($executorUserIdList)) {
            $dbr = $dbr->whereIn('task_member.partner', $executorUserIdList);
        }
        switch ($dateType) {
            case 'delivery':
                $dbr = $dbr->whereBetween('task.plan_end_time', [$startTime, $endTime]);
                break;
            case 'demand_add': // 新增
                $dbr = $dbr->whereBetween('task.created', [$startTime, $endTime])
                    ->whereFieldRaw('get_json_string(task.`json`, "$.task_is_append")', 'yes');
                break;
            case 'delay_created': // 延期
                $rrSql = ReviewRequestModel::where('module_id', ModuleService::getInstance()->byCode('task', 'id'))
                    ->whereBetween('created', $startTime, $endTime)
                    ->buildSql();
                $dbr = $dbr->whereRaw('task.id in (' . $rrSql . ')');
                break;
            case 'delivery_actually': // 实际交付筛选
                $dbr = $dbr->whereBetween('task.delivery_time', [$startTime, $endTime])
                    ->whereFieldRaw('get_json_string(task.`json`, "$.task_status")', StatusService::getInstance()->byCode('final', 'id'));
                break;
            default:
                if (!empty($startTime) && !empty($endTime)) {
                    $dbr = $dbr->whereBetween('task.plan_end_time', [$startTime, $endTime]);
                }
                break;
        }
        if (!empty($projectId)) {
            $dbr = $dbr->where('task.project_id', $projectId);
        }
        if (!empty($taskStepType)) {
            $dbr = $dbr->whereIn('task.step_type', $taskStepType);
        }
        if (!empty($todoStepCategoryId)) {
            if ($standardOrOther == "yes") {
                $departmentUserIdList = DepartmentService::getInstance()->getDepartmentUserIdList(0, $todoStepCategoryId, $request->getTenantId(), 'any');
            } else {
                if (!$rStepCategoryJoined) {
                    $trtJoin = TaskRelationTagModel::whereIn('link_module_code', ['step_category', 'show_step_category'])->buildSql();
                    $dbr = $dbr->join([$trtJoin => 'r_step_category'], 'r_step_category.task_id = task.id');
                    $rStepCategoryJoined = true;
                }
                $dbr = $dbr->whereIn('r_step_category.link_id', $todoStepCategoryId);
            }
        }
        if (!empty($departmentUserIdList)) {
            $dbr = $dbr->whereIn('task_member.partner', $departmentUserIdList);
        }
        if (!empty($onlyNotDone)) {
            $dbr = $dbr->having('task_num > 0 and task_num > final_task_num');
        }
        if (!empty($page) && count($page) > 1) {
            $pageNum = array_shift($page);
            $pageSize = array_shift($page);
            $dbr = $dbr->page($pageNum, $pageSize);
        }

        $res = $dbr->order('user.id')->select()->toArray();
        return result_array_construct([
            'id' => 'intval',
            'name' => 'strval',
            'task_num' => 'intval',
            'final_task_num' => 'intval',
            'repair_num' => 'intval',
            'delay_num' => 'intval',
            'final_work_time' => 'strval',
            'abnormal_num' => 'intval'
        ], $res);
    }

    /**
     * 延期剧集列表
     * @param Request $request
     * @param array $param
     * @return EntityModel[]|array|\think\Collection
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function delayEpisodeList(Request $request, array $param)
    {
        $res = [];
        if (!empty($param['department_id'])) {
            $department = (new DepartmentModel())->where('id', $param['department_id'])->find();
            $param['todo_step_category_id_list'] = explode(',', $department['step_category_ids']);
            if (empty($param['todo_step_category_id_list'])) {
                return $res;
            }
        }
        $closedStatusId = StatusService::getInstance()->byCode('closed')['id'];
        $queryRange = $param['query_range'];
        $queryType = $param['query_type'];
        if ($queryRange == 'upstream') {
            if ($queryType === 'yes') {
                $q = (new DelayOrderTaskModel())
                    ->alias('dot')
                    ->join('delay_order_task_step_category_confirm dotscc', 'dot.id = dotscc.delay_order_task_id')
                    ->join('task', 'task.id = dot.task_id');
                $q = $this->delayCountWhereQuery($q, $param);

                $res = $q
                    ->whereRaw("get_json_string(task.`json`, '$.task_status') <> $closedStatusId")
                    ->where('dotscc.is_self_step_category', 'no')
                    ->where('dotscc.is_confirmed', 'yes')
                    ->group('dot.episode_id')
                    ->field("dot.episode_id,count(distinct dot.task_id) c")
                    ->select();
            } else {
                $q = (new DelayOrderTaskModel())
                    ->alias('dot')
                    ->join('delay_order_task_step_category_confirm dotscc', 'dot.id = dotscc.delay_order_task_id')
                    ->join('task', 'task.id = dot.task_id');
                $q = $this->delayCountWhereQuery($q, $param);
                $res = $q
                    ->whereRaw("get_json_string(task.`json`, '$.task_status') <> $closedStatusId")
                    ->where('dotscc.is_self_step_category', 'no')
                    ->where('dotscc.is_active', 'yes')
                    ->group('dot.episode_id')
                    ->field("dot.episode_id,count(distinct dot.task_id) c")
                    ->select();
            }
        } elseif ($param['query_range'] == 'mine') {
            // 我的
            $mine = (new DelayOrderModel())
                ->alias('do')
                ->join('delay_order_task dot', 'do.id = dot.delay_order_id')
                ->join('task', 'task.id = dot.task_id');
            $mine = $this->delayCountWhereQuery($mine, $param);
            if ($queryType == 'yes') {
                $mine = $mine->whereIn('dot.manager_confirmed', ['yes', 'no']);
            } else {
                $mine = $mine->where('dot.manager_confirmed', 'default');
            }
            $res = $mine
                ->whereRaw("get_json_string(task.`json`, '$.task_status') <> $closedStatusId")
                ->where('dot.created_by', $request->getSaasUserId())
                ->group('dot.episode_id')
                ->field("dot.episode_id,count(dot.task_id) c")->select();
        } else {
            $q = (new DelayOrderTaskModel())
                ->alias('dot')
                ->join('delay_order_task_step_category_confirm dotscc', 'dot.id = dotscc.delay_order_task_id')
                ->join('task', 'task.id = dot.task_id');
            $q = $this->delayCountWhereQuery($q, $param);
            if ($queryType == 'yes') {
                $q = $q->where('dotscc.is_confirmed', 'yes');
            } else {
                $q = $q->where('dotscc.is_confirmed', 'no')->where('dotscc.is_active', 'yes');
            }
            $res = $q
                ->whereRaw("get_json_string(task.`json`, '$.task_status') <> $closedStatusId")
                ->where('dotscc.is_self_step_category', 'yes')
                ->group('dot.episode_id')
                ->field("dot.episode_id,count(dot.task_id) c")->select();
        }

        $episodeCountList = $res->column('c', 'episode_id');
        $episodeIdList = array_keys($episodeCountList);
        if (empty($episodeIdList)) {
            return [];
        }
        $episodeList = (new EntityModel())->whereIn("id", $episodeIdList)->field('id,name,project_id,code')->order('code asc')->select();
        foreach ($episodeList as &$episode) {
            $episode['count'] = $episodeCountList[$episode['id']];
        }
        return $episodeList;
    }

}
